/* usb2Serial.c - USB Serial class driver using the WRS USB2 API */

/*
 * Copyright (c) 2010, 2011, 2013, 2015 Wind River Systems, Inc.
 *
 * The right to copy, distribute, modify, or otherwise make use
 * of this software may be licensed only pursuant to the terms
 * of an applicable Wind River license agreement.
 */


/*
modification history
--------------------
01r,25feb15,j_x  Correct the invalid statement of iosDrvInstall return (VXW6-84185) 
01q,03sep13,wyy  change return value of usb2SerialDelete
01p,01aug13,wyy  Wait for serial common task to be stopped before deleting
                 serial device data (WIND00427945)
01o,02jul13,wyy  Remove compiler warning (WIND00423315)
01n,06may13,s_z  Remove compiler warning (WIND00356717)
01m,28apr13,ghs  Add support of USB message (WIND00411952)
01l,12apr13,ghs  Clear pOwner when open device fail
01k,27feb13,ghs  Force close device after device is removed from IO system
                 (WIND00405575)
01j,26feb13,ghs  Check access mode in read/write functions (WIND00405192)
01i,19feb13,ghs  Add select IO support (WIND00403162)
01h,31jan13,ghs  Fix dead-lock issue (WIND00394547)
01g,04jan13,s_z  Remove compiler warning (WIND00390357)
01f,01aug11,ghs  Fix issue found during code review (WIND00250716)
01e,06jul11,ghs  Improve match function for vendor device (WIND00183500)
01d,07jan11,ghs  Clean up compile warnings (WIND00247082)
01c,02sep10,ghs  Use OS_THREAD_FAILURE to check taskSpawn failure (WIND00229830)
01b,06jul10,m_y  Set all the resources to NULL after release (WIND00183499)
01a,14apr10,ghs  written
*/

/*
DESCRIPTION

This module is the USB and IOS interface module for the USB-to-Serial adapters
built by different Chipsets(Now, support FTDI232, WRS USB CDC) based on WRS
USB 2.x API. This module works as a general higher layer on hardware configure
layer.

The USB-to-Serial devices are all vendor-specific device. This file only supplys
a basic frameworks. Real IO operations are used by registered function pointer
to this frameworks.

This class driver is a client of the Universal Serial Bus Driver (USBD).
All interaction with the USB buses and devices is handled through the USBD.
The driver is designed to be of two architectural features:

Modular, each module is contained in one source file, and everything is
loosely coupled with each other; Implemented with object oriented constructs.

Layered, low level USB I/O is isolated from file system interface code; Use
command and data request information structures to do dispatch the transport
requests.

If USB serial pc console is added, when a USB-to-Serial adapter attached, this
module will check if the adapter is needed to attach as a pc console.

INITIALIZATION

Before the class driver is initialized with usb2SerialInit() with a string
passed in as the base name used for the USB-Ethernet adapter, it is assumed that
USBD is already initialized and at least one USB Host Controller has been
attached successfully.

usb2SerialInit() registers the class driver as a client module with USBD. It
also registers callback routines to get notified whenever a USB-to-Serial
adapter is attached or removed from the system.

NOTE: The usb2SerialInit() is only responsible to setup the driver framework
structure, to enable support for any real USB-to-Serial adapter hardware, call
the hardware device initialization routines (such as usb2SerialFtdi232Init())
BEFORE usb2SerialInit().

DATA STRUCTURES

Following are some data structures used by this class driver:

\is

\i USB2_SERIAL_DEVICE

Basic information for USB serial device

\i USB2_SERIAL_ADAPTER_INFO

Store adapter driver informations, one adapter can contain one or more deviceID.
DeviceID contain vendor specific device information.

\ie

INCLUDE FILES: vxWorks.h logLib.h errnoLib.h iosLib.h
               usb/usbHst.h usb2/usb2Helper.h usb2/usb2Serial.h

*/

/* includes */

#include <vxWorks.h>
#include <logLib.h>
#include <errnoLib.h>
#include <iosLib.h>
#include <usb/usbHst.h>
#include <usb2/usb2Helper.h>
#include <usb2/usb2Serial.h>

/* globals */

IMPORT size_t   usb2SerialRingBfrSize;

IMPORT INT32    usb2SerialTimeOut;

#if !(((_WRS_VXWORKS_MAJOR == 6) && (_WRS_VXWORKS_MINOR >= 9)) || \
      (_WRS_VXWORKS_MAJOR > 6))
UINT32  usb2SerDebug = USB2_CLASS_NONE_DEBUG;
#endif

/* locals */

/* This contain the driver data and some adapter info to register to the usbd */

LOCAL USB2_CLASS_DRIVER *   pUsb2SerialDriver = NULL;

LOCAL int                   usb2SerialDrvNum = 0;

/* link list head of device id */

LOCAL LIST_HEAD             usb2SerialDeviceIDList;

LOCAL SEM_ID                usb2SerialDevIDLstMutex;

/* forward declarations */

LOCAL USBHST_STATUS usb2SerialDeviceAdd(UINT32, UINT8, UINT8, VOID **);

LOCAL VOID          usb2SerialDeviceRemove(UINT32, VOID *);

LOCAL VOID          usb2SerialDeviceSuspend(UINT32, VOID *);

LOCAL VOID          usb2SerialDeviceResume(UINT32, VOID *);

LOCAL USB2_SERIAL_DEVICE *
                    usb2SerialCreate(USB2_CLASS_DEVICE *, UINT8, UINT8, UINT8,
                                     USB2_SERIAL_ADAPTER_INFO *);

LOCAL STATUS        usb2SerialDelete(USB2_SERIAL_DEVICE *);

LOCAL ULONG         usb2SerialIosOpen(DEV_HDR *, char *, int, int);

LOCAL int           usb2SerialIosClose(USB2_FILE_DESC *);

LOCAL ssize_t       usb2SerialIosRead(USB2_FILE_DESC *, char *, int);

LOCAL ssize_t       usb2SerialIosWrite(USB2_FILE_DESC *, char *, UINT32);

LOCAL int           usb2SerialIosIoctl(USB2_FILE_DESC *, int, VOID *);

/*******************************************************************************
*
* usb2SerialInit - register the USB-to-Serial device class driver with the USBD
*
* This routine is called once to register the USB-to-Serial device class driver
* with the USBD. The driver shall be initialized only once.
*
* RETURNS: OK or ERROR if the driver could not be initialized
*
* ERRNO: N/A
*/

STATUS usb2SerialInit
    (
    INT8 *  pName
    )
    {
    USBHST_STATUS hstStatus;

    STATUS        status = ERROR;

    USB2_SER_VDBG("usb2SerialInit(): start...\n", 1, 2, 3, 4, 5, 6);

    if (pName == NULL)
        {
        USB2_SER_ERR("usb2SerialInit(): USB-to-Serial driver need a name\n",
                     1, 2, 3, 4, 5, 6);

        return ERROR;
        }

    if (pUsb2SerialDriver != NULL)
        {
        USB2_SER_ERR("usb2SerialInit(): USB-to-Serial driver already init\n",
                     1, 2, 3, 4, 5, 6);

        return ERROR;
        }

    /* Create a mutext to protect device id list */

    usb2SerialDevIDLstMutex = semMCreate(USB2_MUTEX_CREATION_OPTS);

    if (usb2SerialDevIDLstMutex == SEM_ID_NULL)
        {
        USB2_SER_ERR("usb2SerialInit(): Memory lack for create semphore\n",
                     1, 2, 3, 4, 5, 6);

        goto EXIT;
        }

    /* create driver data structure */

    pUsb2SerialDriver = usb2ClassDriverCreate((char *)pName);

    if (pUsb2SerialDriver == NULL)
        {
        USB2_SER_ERR("usb2SerialInit(): USB-to-Serial driver can not be "
                     "created\n", 1, 2, 3, 4, 5, 6);

        goto EXIT;
        }

    status = usb2TakeDriver(pUsb2SerialDriver);

    if (OK != status)
        {
        USB2_SER_ERR("usb2SerialInit(): Taking Driver Mutex failed\n",
                     1, 2, 3, 4, 5, 6);

        goto EXIT;
        }

    USB2_SER_DBG("usb2SerialInit(): Creating the ioLib driver...\n",
                 1, 2, 3, 4, 5, 6);

    /* install IOS functions */

#if ((_WRS_VXWORKS_MAJOR == 6) && (_WRS_VXWORKS_MINOR >= 9)) || \
    (_WRS_VXWORKS_MAJOR > 6)
    usb2SerialDrvNum = iosDrvInstall
                        ((DRV_CREATE_PTR) NULL,              /* No create */
                         (DRV_REMOVE_PTR) NULL,              /* No delete */
                         (DRV_OPEN_PTR) usb2SerialIosOpen,   /* Open routine */
                         (DRV_CLOSE_PTR) usb2SerialIosClose, /* Close routine */
                         (DRV_READ_PTR) usb2SerialIosRead,   /* Read routine */
                         (DRV_WRITE_PTR) usb2SerialIosWrite, /* Write routine*/
                         (DRV_IOCTL_PTR) usb2SerialIosIoctl);/* Ioctl routine */
#else
    usb2SerialDrvNum = iosDrvInstall
                        ((FUNCPTR) NULL,                /* No create */
                         (FUNCPTR) NULL,                /* No delete */
                         (FUNCPTR) usb2SerialIosOpen,   /* Open routine */
                         (FUNCPTR) usb2SerialIosClose,  /* Close routine */
                         (FUNCPTR) usb2SerialIosRead,   /* Read routine */
                         (FUNCPTR) usb2SerialIosWrite,  /* Write routine*/
                         (FUNCPTR) usb2SerialIosIoctl); /* Ioctl routine */

#endif
    if (ERROR == usb2SerialDrvNum)
        {
        USB2_SER_ERR("usb2SerialInit(): IO driver not installed\n",
                     1, 2, 3, 4, 5, 6);

        goto EXIT;
        }

    /* all USB-to-Serial adapter is VendorSpecific device */

    pUsb2SerialDriver->hstDriver.bFlagVendorSpecific = TRUE;
    pUsb2SerialDriver->hstDriver.uVendorIDorClass = 0;
    pUsb2SerialDriver->hstDriver.uProductIDorSubClass = 0;
    pUsb2SerialDriver->hstDriver.uBCDUSBorProtocol = 0;
    pUsb2SerialDriver->hstDriver.pDeviceList = &usb2SerialDeviceIDList;

    pUsb2SerialDriver->hstDriver.addDevice = usb2SerialDeviceAdd;
    pUsb2SerialDriver->hstDriver.removeDevice = usb2SerialDeviceRemove;
    pUsb2SerialDriver->hstDriver.suspendDevice = usb2SerialDeviceSuspend;
    pUsb2SerialDriver->hstDriver.resumeDevice = usb2SerialDeviceResume;

    /* register to USBD */

    hstStatus = usbHstDriverRegister(&pUsb2SerialDriver->hstDriver, NULL

    /* VxWorks 6.6 and above has a 3rd parameter specfying the name of the driver */

#if ((_WRS_VXWORKS_MAJOR == 6) && (_WRS_VXWORKS_MINOR >= 6)) || \
    (_WRS_VXWORKS_MAJOR > 6)
                                     ,"usbGen2Serial"
#endif
                                    );

    if (hstStatus != USBHST_SUCCESS)
        {
        USB2_SER_ERR("usb2SerialInit(): Cannot register with the USBD...\n",
                     1, 2, 3, 4, 5, 6);

        goto EXIT;
        }

    USB2_SER_VDBG("usb2SerialInit(): Class driver registration succeeded \n",
                  1, 2, 3, 4, 5, 6);

    /* release the driver */

    usb2ReleaseDriver(pUsb2SerialDriver);

    return OK;

EXIT:
    if (usb2SerialDrvNum != 0)
        {
        iosDrvRemove(usb2SerialDrvNum, TRUE);

        usb2SerialDrvNum = 0;
        }

    if (status == OK)
        {
        usb2ReleaseDriver(pUsb2SerialDriver);
        }

    if (pUsb2SerialDriver != NULL)
        {
        usb2ClassDriverDelete(pUsb2SerialDriver);

        pUsb2SerialDriver = NULL;
        }

    if (usb2SerialDevIDLstMutex != SEM_ID_NULL)
        {
        (void) semTake(usb2SerialDevIDLstMutex, WAIT_FOREVER);

        (void) semDelete(usb2SerialDevIDLstMutex);

        usb2SerialDevIDLstMutex = SEM_ID_NULL;
        }

    return ERROR;
    }

/*******************************************************************************
*
* usb2SerialLockDevIDList - lock vendor device id list
*
* This routine takes mutex to lock and protect vendor device id list, if mutex
* is not initialized, also consider taking operation success.
*
* RETURNS: OK or ERROR if error occured
*
* ERRNO: N/A
*/

STATUS usb2SerialLockDevIDList()
    {
    if (usb2SerialDevIDLstMutex != NULL)
        return semTake(usb2SerialDevIDLstMutex, WAIT_FOREVER);

    return OK;
    }

/*******************************************************************************
*
* usb2SerialUnLockDevIDList - unlock vendor device id list
*
* This routine releases mutex to unlock vendor device id list, if mutex is not
* initialized, also consider taking operation success.
*
* RETURNS: N/A
*
* ERRNO: N/A
*/

VOID usb2SerialUnLockDevIDList()
    {
    if (usb2SerialDevIDLstMutex != NULL)
        semGive(usb2SerialDevIDLstMutex);

    return;
    }

/*******************************************************************************
*
* usb2SerialDriverListAdd - add adapter drivers to USB-to-Serial driver list
*
* This routine is called BEFORE usb2SerialInit. Use this routine to add adapter
* drivers to USB-to-Serial driver list.
*
* RETURNS: OK or ERROR if the adapter driver could not be added
*
* ERRNO: N/A
*/

STATUS usb2SerialDeviceIdAdd
    (
    USB2_SERIAL_DEVICE_ID *     pDeviceIdList,
    USB2_SERIAL_ADAPTER_INFO *  pAdaptor,
    UINT16                      uTeamMembers
    )
    {
    UINT32 i;

    if (pAdaptor == NULL || pDeviceIdList == NULL || uTeamMembers == 0)
        {
        USB2_SER_ERR("usb2SerialDeviceIdAdd(): Parameter error\n",
                     1, 2, 3, 4, 5, 6);

        return ERROR;
        }

    if (usb2SerialLockDevIDList() != OK)
        {
        USB2_SER_ERR("usb2SerialDeviceIdAdd(): Fail to take lock\n",
                     1, 2, 3, 4, 5, 6);

        return ERROR;
        }

    for (i = 0; i < uTeamMembers; i++)
        {
        if (pDeviceIdList[i].uMatchFlag != 0)
            {
            usbListLink(&usb2SerialDeviceIDList, &pDeviceIdList[i],
                        &pDeviceIdList[i].link, LINK_HEAD);

            pDeviceIdList[i].pUserData = pAdaptor;
            }
        }

    usb2SerialUnLockDevIDList();

    return OK;
    }

/*******************************************************************************
*
* usb2SerialDeviceIdRemove - remove adapter drivers from vendor device list
*
* This routine removes adapter drivers to vendor device list.
*
* RETURNS: OK for removing success or adaptor not in the list, ERROR
*          if parameter error.
*
* ERRNO: N/A
*/

STATUS usb2SerialDeviceIdRemove
    (
    USB2_SERIAL_ADAPTER_INFO *  pAdaptor
    )
    {
    UINT32 i;
    USB2_SERIAL_DEVICE_ID * pDevId, * pTmpId;

    if (pAdaptor == NULL)
        {
        USB2_SER_ERR("usb2SerialDeviceIdAdd(): Parameter error\n",
                     1, 2, 3, 4, 5, 6);

        return ERROR;
        }

    if (usb2SerialLockDevIDList() != OK)
        {
        USB2_SER_ERR("usb2SerialDeviceIdAdd(): Fail to take lock\n",
                     1, 2, 3, 4, 5, 6);

        return ERROR;
        }

    pDevId = usbListFirst(&usb2SerialDeviceIDList);

    while(pDevId != NULL)
        {
        if (pDevId->pUserData == pAdaptor)
            {
            pTmpId = pDevId;
            pDevId = usbListNext(&pDevId->link);
            usbListUnlink(&pTmpId->link);
            }
        else
            {
            pDevId = usbListNext(&pDevId->link);
            }
        }

    usb2SerialUnLockDevIDList();

    return OK;
    }

/*******************************************************************************
*
* usb2SerialDeviceIdMatch - try to match a device ID in the adapter driver list
*
* This routine is used to match a device ID in the adapter driver list. If it
* find a matched vendor, this routine return the adapter driver pointer.
*
* RETURNS: Device id node pointer.
*
* ERRNO: N/A
*/

USB2_SERIAL_DEVICE_ID * usb2SerialDeviceIdMatch
    (
    pUSBD_DEVICE_INFO  pDeviceInfo,
    pUSBD_INTERFACE_INFO pInterfaceInfo
    )
    {
    USB2_SERIAL_DEVICE_ID devId;

    OS_MEMSET(&devId, 0, sizeof(devId));

    if (pDeviceInfo != NULL)
        {
        devId.uVendorID = pDeviceInfo->uVendorID;
        devId.uProductID = pDeviceInfo->uDeviceID;
        devId.uBCDDevice = pDeviceInfo->uBCDDevice;

        devId.uDeviceClass = pDeviceInfo->uDeviceClass;
        devId.uDeviceSubClass = pDeviceInfo->uDeviceSubClass;
        devId.uDeviceProtocol = pDeviceInfo->uDeviceProtocol;
        }

    if (pInterfaceInfo != NULL)
        {
        devId.bInterfaceClass = pInterfaceInfo->uInterfaceClass;
        devId.bInterfaceSubClass = pInterfaceInfo->uInterfaceSubClass;
        devId.bInterfaceProtocol = pInterfaceInfo->uInterfaceProtocol;
        }

    return usbdMatchDriverId(&devId, usbListFirst(&usb2SerialDeviceIDList));
    }

/*******************************************************************************
*
* usb2SerialDeviceAdd - add serial device callback function
*
* This routine is called by USB GEN2 driver when a USB-to-Serial device adapter
* attached. This routine create device data structure and add device to IOS lib.
*
* RETURNS: OK or ERROR if the device could not be added
*
* ERRNO: N/A
*
* \NOMANUAL
*/

LOCAL USBHST_STATUS usb2SerialDeviceAdd
    (
    UINT32  hDevice,             /* device handler returned by usbd */
    UINT8   bInterfaceNumber,    /* interface number found by the usbd */
    UINT8   uSpeed,              /* speed of the device */
    VOID ** ppDriverData         /* place to put the device data*/
    )
    {
    USB2_SERIAL_ADAPTER_INFO *      pDriverInfo = NULL;

    pUSBD_DEVICE_INFO               pDeviceInfo = NULL;
    pUSBD_INTERFACE_INFO            pInterfaceInfo = NULL;

    USB2_SERIAL_DEVICE *            pDevice = NULL;
    USB2_CLASS_DEVICE *             pUsb2ClassDevice = NULL;

    USB2_SERIAL_DEVICE_CONFIG_INFO  ConfigInfo;

    STATUS                          status = ERROR;
    USBHST_STATUS                   hstStatus;
    USB2_SERIAL_DEVICE_ID *         pDrvDevId;

    /* get the device information, for the descriptors */

    usbdTranslateDeviceHandle(hDevice, &pDeviceInfo);

    if (NULL == pDeviceInfo)
        {
        USB2_SER_ERR("usb2SerialDeviceAdd(): Device is not exist\n",
                     1, 2, 3, 4, 5, 6);

        return USBHST_FAILURE;
        }

    /* scan the global list to check whether the device is supported */

    pInterfaceInfo = &pDeviceInfo->pInterfacesInfo[bInterfaceNumber];

    pDrvDevId = usb2SerialDeviceIdMatch(pDeviceInfo, pInterfaceInfo);

    if (pDrvDevId == NULL)
        {
        USB2_SER_ERR("usb2SerialDeviceAdd(): Match failed for device\n",
                     1, 2, 3, 4, 5, 6);

        return USBHST_FAILURE;
        }

    pDriverInfo = pDrvDevId->pUserData;

    if (pDriverInfo->privateFuncs.usb2SerialParseConfig == NULL)
        {
        USB2_SER_ERR("usb2SerialDeviceAdd(): Device driver do not have add "
                     "method\n", 1, 2, 3, 4, 5, 6);

        return USBHST_FAILURE;
        }

    /*
     * NOTE:
     *   use adapter drvier to parse config, because different device may have
     *   different config data. Now we only get 6 value from config function.
     *   If need more value, should change this structure.
     */

    OS_MEMSET(&ConfigInfo, 0, sizeof(USB2_SERIAL_DEVICE_CONFIG_INFO));

    hstStatus =
        pDriverInfo->privateFuncs.usb2SerialParseConfig(pDriverInfo,
                                                        hDevice,
                                                        bInterfaceNumber,
                                                        &ConfigInfo);

    if (hstStatus == USBHST_FAILURE)
        {
        USB2_SER_ERR("usb2SerialDeviceAdd(): Parse config fail\n",
                     1, 2, 3, 4, 5, 6);

        return USBHST_FAILURE;
        }

    status = usb2TakeDriver(pUsb2SerialDriver);

    if (OK != status)
        {
        USB2_SER_ERR("usb2SerialDeviceAdd(): Mutex semaphore Not taken\n",
                     1, 2, 3, 4, 5, 6);

        return USBHST_FAILURE;
        }

    USB2_SER_DBG("usb2SerialDeviceAdd(): Building & Filling the "
                 "Device structure...\n", 1, 2, 3, 4, 5, 6);

    /* create the usb2 class device structure */

    pUsb2ClassDevice = usb2ClassDeviceCreate(pUsb2SerialDriver,
                                             hDevice,
                                             ConfigInfo.configurationIndex,
                                             bInterfaceNumber,
                                             ConfigInfo.uAlternateSetting);

    if (NULL == pUsb2ClassDevice)
        {
        usb2ReleaseDriver(pUsb2SerialDriver);

        return USBHST_FAILURE;
        }

    USB2_SER_DBG("usb2SerialDeviceAdd(): Create the USB-Serial device "
                 "structure\n", 1, 2, 3, 4, 5, 6);

    /* create serial device structure */

    pDevice = usb2SerialCreate(pUsb2ClassDevice,
                               ConfigInfo.uInEndPointAddr,
                               ConfigInfo.uOutEndPointAddr,
                               ConfigInfo.uSetupEndPointAddr,
                               pDriverInfo);

    if (NULL == pDevice)
        {
        USB2_SER_ERR("usb2SerialDeviceAdd(): Could not create device "
                     "structure\n", 1, 2, 3, 4, 5, 6);

        usb2ClassDeviceDelete(pUsb2ClassDevice);
        pUsb2ClassDevice = NULL;
        usb2ReleaseDriver(pUsb2SerialDriver);
        return USBHST_FAILURE;
        }

    pDevice->wMaxPacketSizeOut = ConfigInfo.wMaxPacketSizeOut;
    pDevice->wMaxPacketSizeIn = ConfigInfo.wMaxPacketSizeIn;
    pDevice->wMaxPacketSizeSetup = ConfigInfo.wMaxPacketSizeSetup;

    /* save device id and interface number for multi-port serial devices use */
    pDevice->devId = pDrvDevId;
    pDevice->interfaceNum = bInterfaceNumber;

    USB2_SER_DBG("usb2SerialDeviceAdd(): Adding device %p\n",
                 (ULONG)pDevice, 2, 3, 4, 5, 6);

    pDevice->pDriverInfo = pDriverInfo;

    USB2_SER_DBG("usb2SerialDeviceAdd(): Adding device %s to IO driver %d\n",
                 pDevice->pUsb2ClassDevice->pDeviceName,
                 usb2SerialDrvNum, 3, 4, 5, 6);

    /* install ios device */

    status = iosDevAdd(&pDevice->deviceHeader,
                       pDevice->pUsb2ClassDevice->pDeviceName,
                       usb2SerialDrvNum);

    if (OK != status)
        {
        USB2_SER_ERR("usb2SerialDeviceAdd(): iosDevAdd failed\n",
                     1, 2, 3, 4, 5, 6);
        usb2ClassDeviceDelete(pUsb2ClassDevice);
        pUsb2ClassDevice = NULL;
        usb2ReleaseDriver(pUsb2SerialDriver);
        usb2SerialDelete(pDevice);

        return USBHST_FAILURE;
        }

    /* store the device name */

    pDevice->pDeviceGeneralName =
        OSS_MALLOC(OS_STRLEN(pDevice->deviceHeader.name) + 1);

    if (pDevice->pDeviceGeneralName != NULL)
        strncpy(pDevice->pDeviceGeneralName, pDevice->deviceHeader.name,
                OS_STRLEN(pDevice->deviceHeader.name));

    USB2_SER_DBG("usb2SerialDeviceAdd(): Serial device %s is up and running\n",
                 pDevice->deviceHeader.name,2,3,4,5,6);

    *ppDriverData = pDevice;

    printf("Find USB-to-Serial adapter device: %s\n",
           pDevice->pDriverInfo->deviceName);

    printf("Added new USB-to-Serial adapter device as %s\n",
           pDevice->deviceHeader.name);

    /* if adapter driver have private create callback, call it */

    if (pDriverInfo->privateFuncs.usb2SerialDeviceCreateCallback != NULL)
        {
        /*
         * the create callback use to finish some vendor specific issue after
         * device create
         */

        pDriverInfo->privateFuncs.usb2SerialDeviceCreateCallback(pDevice);
        }

    /* check if need to attach pc console */

    usrUsb2SerialAttachPcCon(pDevice);

    /* release the driver */

    usb2ReleaseDriver(pUsb2SerialDriver);

    usbMsgPost(USBMSG_GEN2_SER_DEVREADY,
               pDevice,
               pDevice->pUsb2ClassDevice->pDeviceName);

    /* all done return */

    return USBHST_SUCCESS;
    }

/*******************************************************************************
*
* usb2SerialDeleteCallback - delete serial device callback function
*
* This routine is called by USB GEN2 driver as a callback function. Call
* usb2SerialDelete to delete device resources.
*
* RETURNS: OK
*
* ERRNO: N/A
*
* \NOMANUAL
*/

LOCAL STATUS usb2SerialDeleteCallback
    (
    USB2_SERIAL_DEVICE * pDevice
    )
    {
    /* delete data structure */

    usb2SerialDelete(pDevice);

    return OK;
    }

/*******************************************************************************
*
* usb2SerialDeviceRemove - remove serial device callback function
*
* This routine is called by USB GEN2 driver as a callback function when device
* remove. This routine delete device from IO system and detach device from pc
* console if configured.
*
* RETURNS: N/A
*
* ERRNO: N/A
*
* \NOMANUAL
*/

LOCAL VOID usb2SerialDeviceRemove
    (
    UINT32 hDevice,      /* Device handler provided by the USBD */
    VOID * pDriverData   /* Driver structure returned by add routine */
    )
    {
    STATUS                      status      = ERROR;
    USB2_SERIAL_DEVICE *        pDevice     = NULL;
    USB2_SERIAL_ADAPTER_INFO *  pDriverInfo = NULL;

    if ((NULL == (pDevice = (USB2_SERIAL_DEVICE *)pDriverData)) ||
        (NULL == (pDriverInfo = pDevice->pDriverInfo)))
        {
        USB2_SER_ERR("usb2SerialDeviceRemove(): Parameter error\n",
                     1, 2, 3, 4, 5, 6);

        return;
        }

    USB2_SER_DBG("usb2SerialDeviceRemove(): Removing USB Serial device %p\n",
                 pDevice, 2, 3, 4, 5, 6);

    usbMsgPost(USBMSG_GEN2_SER_DEVREMOVED,
               pDevice,
               pDevice->pUsb2ClassDevice->pDeviceName);

    /* try to take the device */

    status = usb2TakeDriver(pUsb2SerialDriver);

    if (OK != status)
        {
        USB2_SER_ERR("usb2SerialDeviceRemove(): Cannot take driver ownership\n",
                     1, 2, 3, 4, 5, 6);

        return;
        }

    status = usb2TakeDevice(pDevice->pUsb2ClassDevice);

    if (OK != status)
        {
        USB2_SER_ERR("usb2SerialDeviceRemove(): Cannot take device ownership\n",
                     1, 2, 3, 4, 5, 6);

        usb2ReleaseDriver(pUsb2SerialDriver);

        return;
        }

    /* if adapter driver have private remove callback, call it */

    if (pDriverInfo->privateFuncs.usb2SerialDeviceRemoveCallback != NULL)
        {
        pDriverInfo->privateFuncs.usb2SerialDeviceRemoveCallback(pDevice);
        }

    /* detach from pc console if configured */

    usrUsb2SerialDetachPcCon(pDevice);

    /* delete device in IOS */

    if (iosDevDelete(&pDevice->deviceHeader) != OK)
        {
        USB2_SER_ERR("usb2SerialDeviceRemove(): IOS device delete failed\n",
                     1, 2, 3, 4, 5, 6);
        }

    /* force to close device */

    if (pDevice->pOwner != NULL)
        {
        usb2SerialIosClose(pDevice->pOwner);
        }

    /* post delete message */

    usb2DeviceDeleteMsgPost(
        pDevice->pUsb2ClassDevice,
        usb2SerialDeleteCallback,
        pDevice
        );

    USB2_SER_DBG("usb2SerialDeviceRemove(): Now delete the device\n",
                 1, 2, 3, 4, 5, 6);

    usb2ReleaseDevice(pDevice->pUsb2ClassDevice);

    usb2ReleaseDriver(pUsb2SerialDriver);

    return;
    }

/*******************************************************************************
*
* usb2SerialDeviceSuspend - suspend serial device callback function
*
* This routine is called by USB GEN2 driver as a callback function to suspend
* device. Right now there is nothing to do, so this routine simply returns.
*
* RETURNS: N/A
*
* ERRNO: N/A
*
* \NOMANUAL
*/

LOCAL VOID usb2SerialDeviceSuspend
    (
    UINT32 hDevice,
    VOID * pDriver
    )
    {
    USB2_SER_ERR("usb2SerialDeviceSuspend(): UNSUPPORTED...\n",
                 1, 2, 3, 4, 5, 6);
    }

/*******************************************************************************
*
* usb2SerialDeviceSuspend - resume serial device callback function
*
* This routine is called by USB GEN2 driver as a callback function to resume
* device. Right now there is nothing to do, so this routine simply returns.
*
* RETURNS: N/A
*
* ERRNO: N/A
*
* \NOMANUAL
*/

LOCAL VOID usb2SerialDeviceResume
    (
    UINT32 hDevice,
    VOID * pDriver
    )
    {
    USB2_SER_ERR("usb2SerialDeviceResume(): UNSUPPORTED...\n",
                 1, 2, 3, 4, 5, 6);
    }

/*******************************************************************************
*
* usb2SerialIosOpen - IO open call
*
* This routine is used for IO system to open a USB-to-Serial device. Alloc file
* descriptor and check the parameter.
*
* One serial device only allow one owner to operate it, this routine will store
* the owner informations. This limitation does not effect PC console or tty lib.
*
* For vendor specific device this routine call adapter driver funciton to finish
* the vendor specific operations.
*
* RETURNS: File descriptor or ERROR if error occurs
*
* ERRNO: EACCES, EINVAL, ENOMEM
*
* \NOMANUAL
*/

LOCAL ULONG usb2SerialIosOpen
    (
    DEV_HDR *   pDevHdr,
    char *      notUsed,
    int         flags,
    int         mode
    )
    {
    STATUS                  status;
    USB2_FILE_DESC *        pFd;
    USB2_SERIAL_DEVICE *    pDevice = (USB2_SERIAL_DEVICE *) pDevHdr;

    /* check parameter */

    if (pDevice == NULL)
        {
        USB2_SER_ERR("usb2SerialIosOpen(): Parameter error\n",
                     1, 2, 3, 4, 5, 6);

        errnoSet(EINVAL);
        return (ULONG)ERROR;
        }

    USB2_SER_VDBG("usb2SerialIosOpen(): pDevice %p Name %s\n",
                  pDevice, pDevice->pUsb2ClassDevice->pDeviceName, 3, 4, 5, 6);

    /* take device before operate it */

    status = usb2TakeDevice(pDevice->pUsb2ClassDevice);

    if (OK != status)
        {
        USB2_SER_ERR("usb2SerialIosOpen(): Can not take device\n",
                     1, 2, 3, 4, 5, 6);

        errnoSet(EACCES);
        return (ULONG)ERROR;
        }

    /* alloc file descriptor */

    pFd = (USB2_FILE_DESC*) OSS_CALLOC(sizeof(USB2_FILE_DESC));

    if (!pFd)
        {
        USB2_SER_ERR("usb2SerialIosOpen(): Cannot allocate data\n",
                     1, 2, 3, 4, 5, 6);

        errnoSet(ENOMEM);
        usb2ReleaseDevice(pDevice->pUsb2ClassDevice);
        return (ULONG)ERROR;
        }

    pFd->pDevice = pDevice;
    pFd->fdType = flags;

    if (O_CREAT & flags)
        {
        USB2_SER_ERR("usb2SerialIosOpen(): Device not creatable\n",
                     1, 2, 3, 4, 5, 6);

        OSS_FREE(pFd);
        errnoSet(EINVAL);
        usb2ReleaseDevice(pDevice->pUsb2ClassDevice);
        return (ULONG)ERROR;
        }

    /* the USB-to-Serial device can only have one owner */

    if (NULL != pDevice->pOwner)
        {
        USB2_SER_ERR("usb2SerialIosOpen(): Only one operator to be supported\n",
                     1, 2, 3, 4, 5, 6);

        OSS_FREE(pFd);
        errnoSet(EINVAL);
        usb2ReleaseDevice(pDevice->pUsb2ClassDevice);

        return (ULONG)ERROR;
        }

    pDevice->pOwner = pFd;

    if (pDevice->pDriverInfo->privateFuncs.usb2SerialOpen)
        {
        /* call private function to finish vendor specific command */

        if (pDevice->pDriverInfo->privateFuncs.usb2SerialOpen(pDevice, mode)
            != OK)
            {
            USB2_SER_ERR("usb2SerialIosOpen(): Open error\n", 1, 2, 3, 4, 5, 6);

            pDevice->pOwner = NULL;

            OSS_FREE(pFd);
            errnoSet(EINVAL);
            usb2ReleaseDevice(pDevice->pUsb2ClassDevice);

            return (ULONG)ERROR;
            }
        }

    usb2ReleaseDevice(pDevice->pUsb2ClassDevice);

    return (ULONG)pFd;
    }

/*******************************************************************************
*
* usb2SerialIosClose - IO close call
*
* This routine is used for IO system to close a USB-to-Serial device
*
* This routine check file descriptor to make sure that it must be closed by its
* owner. For vendor specific device this routine call adapter driver funciton to
* finish the vendor specific operations.
*
* RETURNS: OK or ERROR if error occurs
*
* ERRNO: N/A
*
* \NOMANUAL
*/

LOCAL int usb2SerialIosClose
    (
    USB2_FILE_DESC * pFd
    )
    {
    USB2_SERIAL_DEVICE * pDevice = pFd->pDevice;

    /* check parameter */

    if (pDevice == NULL)
        {
        USB2_SER_ERR("usb2SerialIosClose(): Parameter error\n",
                     1, 2, 3, 4, 5, 6);

        return ERROR;
        }

    USB2_SER_VDBG("usb2SerialIosClose(): Closing the USB-to-Serial device %s\n",
                  pDevice->deviceHeader.name, 2, 3, 4, 5, 6);

    /* take device before operate it */

    if (OK != usb2TakeDevice(pDevice->pUsb2ClassDevice))
        {
        USB2_SER_ERR("usb2SerialIosClose(): Cannot take device ownership\n",
                     1, 2, 3, 4, 5, 6);

        return ERROR;
        }

    /* check the owner, only owner can close the device */

    if (pDevice->pOwner == pFd)
        {
        USB2_SER_VDBG("usb2SerialIosClose(): Reset device owner to NULL\n",
                      1, 2, 3, 4, 5, 6);

        pDevice->pOwner = NULL;
        }
    else
        {
        USB2_SER_ERR("usb2SerialIosClose(): Access fail\n", 1, 2, 3, 4, 5, 6);

        usb2ReleaseDevice(pDevice->pUsb2ClassDevice);
        return ERROR;
        }

    if (pDevice->pDriverInfo->privateFuncs.usb2SerialClose)
        {
        /* call private function to finish vendor specific command */

        if (pDevice->pDriverInfo->privateFuncs.usb2SerialClose(pDevice) != OK)
            {
            USB2_SER_ERR("usb2SerialIosClose(): Close error\n",
                         1, 2, 3, 4, 5, 6);

            usb2ReleaseDevice(pDevice->pUsb2ClassDevice);
            return ERROR;
            }
        }

    OSS_FREE(pFd);

    usb2ReleaseDevice(pDevice->pUsb2ClassDevice);

    return OK;
    }

/*******************************************************************************
*
* usb2SerialIosRead - IO read call
*
* This routine is used for IO system to read data from a USB-to-Serial device
*
* This routine read data from device. It calls adapter driver to finish this
* work.
*
* RETURNS: actually read data size or ERROR if error occurs
*
* ERRNO: EACCES
*
* \NOMANUAL
*/

LOCAL ssize_t usb2SerialIosRead
    (
    USB2_FILE_DESC *        pFd,
    char *                  pBuffer,
    int                     bfrSize
    )
    {
    ssize_t                 ret = ERROR;
    USB2_SERIAL_DEVICE *    pDevice = (USB2_SERIAL_DEVICE *)pFd->pDevice;

    /* check parameter */

    if (pDevice == NULL)
        {
        USB2_SER_ERR("usb2SerialIosRead(): Parameter error\n",
                     1, 2, 3, 4, 5, 6);

        return ret;
        }

    /* check access mode */

    if ((pFd->fdType & O_ACCMODE) == O_WRONLY)
        {
        errnoSet(EACCES);
        return ERROR;
        }

    /* take device before operate it */

    if (OK != usb2TakeDevice(pDevice->pUsb2ClassDevice))
        {
        USB2_SER_ERR("usb2SerialIosRead(): Cannot take device ownership\n",
                     1, 2, 3, 4, 5, 6);

        return ERROR;
        }

    /* call private function to finish operation */

    if (pDevice->pDriverInfo->privateFuncs.usb2SerialRead)
        {
        ret = pDevice->pDriverInfo->privateFuncs.usb2SerialRead(pDevice,
                                                                pBuffer,
                                                                bfrSize);
        }

    usb2ReleaseDevice(pDevice->pUsb2ClassDevice);

    return ret;
    }

/*******************************************************************************
*
* usb2SerialIosWrite - IO write call
*
* This routine is used for IO system to write data data a USB-to-Serial device.
* It calls adapter driver to finish this work.
*
* RETURNS: actually write data size or ERROR if error occurs
*
* ERRNO: EACCES
*
* \NOMANUAL
*/

LOCAL ssize_t usb2SerialIosWrite
    (
    USB2_FILE_DESC *        pFd,
    char *                  pBuffer,
    UINT32                  bfrSize
    )
    {
    ssize_t                 ret = ERROR;
    USB2_SERIAL_DEVICE *    pDevice = (USB2_SERIAL_DEVICE *)pFd->pDevice;

    /* check parameter */

    if (pDevice == NULL)
        {
        USB2_SER_ERR("usb2SerialIosWrite(): Parameter error\n",
                     1, 2, 3, 4, 5, 6);

        return ret;
        }

    /* check access mode */

    if ((pFd->fdType & O_ACCMODE) == O_RDONLY)
        {
        errnoSet(EACCES);
        return ERROR;
        }

    /* take device before operate it */

    if (OK != usb2TakeDevice(pDevice->pUsb2ClassDevice))
        {
        USB2_SER_ERR("usb2SerialIosWrite(): Cannot take device ownership\n",
                     1, 2, 3, 4, 5, 6);

        return ERROR;
        }

    /* call private function to finish operation */

    if (pDevice->pDriverInfo->privateFuncs.usb2SerialWrite)
        {
        ret = pDevice->pDriverInfo->privateFuncs.usb2SerialWrite(pDevice,
                                                                 pBuffer,
                                                                 bfrSize);
        }

    usb2ReleaseDevice(pDevice->pUsb2ClassDevice);

    return ret;
    }

/*******************************************************************************
*
* usb2SerialIosIoctl - IO controll call
*
* This routine is used for IO system to set IO control for USB-to-Serial device.
* It calls adapter driver to finish this work.
*
* RETURNS: OK or ERROR if error occurs
*
* ERRNO: N/A
*
* \NOMANUAL
*/

LOCAL int usb2SerialIosIoctl
    (
    USB2_FILE_DESC *        pFd,
    int                     function,
    VOID *                  arg
    )
    {
    STATUS                  ret = ERROR;
    USB2_SERIAL_DEVICE *    pDevice = (USB2_SERIAL_DEVICE *)pFd->pDevice;

    /* check parameter */

    if (pDevice == NULL)
        {
        USB2_SER_ERR("usb2SerialIosIoctl(): Parameter error\n",
                     1, 2, 3, 4, 5, 6);

        return ret;
        }

    /* call private function to finish operation */

    if (pDevice->pDriverInfo->privateFuncs.usb2SerialIoctl)
        {
        ret = pDevice->pDriverInfo->privateFuncs.usb2SerialIoctl(pDevice,
                                                                 function,
                                                                 arg);
        }

    return ret;
    }

/*******************************************************************************
*
* usb2SerialUrbCompleteCallback - complete callback for a urb sent complete
*
* This routine is a callback function for a urb sent complete. Underrun error or
* overrun error will be ignore in this routine because it is acceptable.
*
* RETURNS: USBHST_SUCCESS, USBHST_INVALID_PARAMETER or urb status
*
* ERRNO: N/A
*
* \NOMANUAL
*/

LOCAL USBHST_STATUS usb2SerialUrbCompleteCallback
    (
    pUSBHST_URB             pUrb
    )
    {
    USBHST_STATUS           Result;

    if (NULL == pUrb || NULL == pUrb->pContext)
        {
        USB2_SER_ERR("usb2SerialUrbCompleteCallback(): parameter error\n",
                     1, 2, 3, 4, 5, 6);

        return USBHST_INVALID_PARAMETER;
        }

    /* ignore data underrun error or data overrun error */

    if (pUrb->nStatus == USBHST_DATA_UNDERRUN_ERROR
        || pUrb->nStatus == USBHST_DATA_OVERRUN_ERROR)
        pUrb->nStatus = USBHST_SUCCESS;

    Result = pUrb->nStatus;

    /* release event signal that this urb has been transferred */

    OS_RELEASE_EVENT(pUrb->pContext);

    if (USBHST_SUCCESS != Result)
        {
        USB2_SER_ERR("usb2SerialUrbCompleteCallback(): URB %p Failed, Result"
                     "=%d\n", pUrb, Result, 3, 4, 5, 6);

        return Result;
        }

    USB2_SER_VDBG("setup urb finish\n", 1, 2, 3, 4, 5, 6);

    return USBHST_SUCCESS;
    }

/*******************************************************************************
*
* usb2SerialSubmitSetupUrb - submit a setup urb for a USB-to-Serial adapter
*
* This routine is used for sending a setup urb request for a USB-to-Serial
* adapter to USBD.
*
* RETURNS: OK or ERROR if the packet error
*
* ERRNO: N/A
*/

STATUS usb2SerialSubmitSetupUrb
    (
    USB2_SERIAL_DEVICE *    pDevice,
    UINT8                   uRequestType,
    UINT8                   uRequest,
    UINT16                  wValue,
    UINT16                  wPort,
    UINT16 *                pwLength,
    UINT8 *                 pBuffer
    )
    {
    pUSBHST_SETUP_PACKET    pSetupPacket   = NULL;
    USBHST_STATUS           Result         = USBHST_FAILURE;
    OS_EVENT_ID             EventId;

    /* check parameters */

    logMsg("Run step1\n",1,2,3,4,5,6);
    if (pDevice == NULL
        || pwLength == NULL
        || (*pwLength == 0 && pBuffer != NULL)
        || (*pwLength != 0 && pBuffer == NULL))
        {
        USB2_SER_ERR("usb2SerialSubmitSetupUrb(): Parameter error\n",
                     1, 2, 3, 4, 5, 6);
        logMsg("Run step2\n",1,2,3,4,5,6);
        return ERROR;
        }

    pSetupPacket = OSS_CALLOC(sizeof(USBHST_SETUP_PACKET));

    if (NULL == pSetupPacket)
        {
        USB2_SER_ERR("usb2SerialSubmitSetupUrb(): Memory insuff "
                     "- pSetupPacket\n", 1, 2, 3, 4, 5, 6);
        logMsg("Run step3\n",1,2,3,4,5,6);
        return ERROR;
        }

    /* create and fill the request */

    USBHST_FILL_SETUP_PACKET(pSetupPacket,
                             uRequestType,
                             uRequest,
                             wValue,
                             wPort,
                             *pwLength);

    /* create event for request */

    if (!(EventId = OS_CREATE_EVENT(OS_EVENT_NON_SIGNALED)))
        {
        USB2_SER_ERR("usb2SerialSubmitSetupUrb(): Sem create failed\n",
                     1, 2, 3, 4, 5, 6);

        OS_FREE(pSetupPacket);

        logMsg("Run step4\n",1,2,3,4,5,6);
        return ERROR;
        }

    USBHST_FILL_CONTROL_URB(pDevice->pSetupUrb,
                            pDevice->pUsb2ClassDevice->hDevice,
                            0,
                            pBuffer,
                            *pwLength,
                            USBHST_SHORT_TRANSFER_OK,
                            pSetupPacket,
                            usb2SerialUrbCompleteCallback,
                            EventId,
                            USBHST_SUCCESS);

    Result = usbHstURBSubmit(pDevice->pSetupUrb);

    if (USBHST_SUCCESS != Result)
        {
        USB2_SER_ERR("usb2SerialSubmitSetupUrb(): Submit request failed\n",
                     1, 2, 3, 4, 5, 6);

        OS_DESTROY_EVENT(EventId);
        OS_FREE(pSetupPacket);
        return Result;
        }

    /* wait for the event */
    if (OS_WAIT_FOR_EVENT(EventId,
            OS_CONVERT_MILLISECONDS_TO_WAIT_VALUE(pDevice->deviceTimeout))
        == ERROR)
        {
        USB2_SER_ERR("usb2SerialSubmitSetupUrb(): Timeout\n", 1, 2, 3, 4, 5, 6);

        (void) usbHstURBCancel(pDevice->pSetupUrb);

        pDevice->pSetupUrb->pContext = NULL;

        OS_DESTROY_EVENT(EventId);
        OS_FREE(pSetupPacket);

        logMsg("Run step5\n",1,2,3,4,5,6);
        return ERROR;
        }

    OS_DESTROY_EVENT(EventId);
    OS_FREE(pSetupPacket);

    *pwLength = (UINT16)pDevice->pSetupUrb->uTransferLength;

    USB2_SER_DBG("usb2SerialSubmitSetupUrb(): Submit successful\n",
                 1, 2, 3, 4, 5, 6);
    logMsg("Run step6\n",1,2,3,4,5,6);
    return OK;
    }

/*******************************************************************************
*
* usb2SerialBulkTransfer - submit a standard bulk transfer for device
*
* This routine is used for start a standard bulk transfer for a USB-to-Serial
* adapter device.
*
* RETURNS: OK or ERROR if some error occured
*
* ERRNO: EIO
*/

STATUS usb2SerialBulkTransfer
    (
    USB2_SERIAL_DEVICE *    pDevice,
    pUSBHST_URB             pUrb,
    UINT8                   bEndPointAddr,
    UINT32 *                pTransferLength,
    UINT8 *                 pBuffer
    )
    {
    USBHST_STATUS   hstStatus = USBHST_FAILURE;
    OS_EVENT_ID     eventId;

    /* check parameters */

    if (pDevice == NULL || pTransferLength == NULL || pBuffer == NULL)
        {
        USB2_SER_ERR("usb2SerialBulkTransfer(): Parameter error\n",
                     1, 2, 3, 4, 5, 6);

        return ERROR;
        }

    if (!(eventId = OS_CREATE_EVENT(OS_EVENT_NON_SIGNALED)))
        {
        USB2_SER_ERR("usb2SerialBulkTransfer(): Sem create failed \n",
                     1, 2, 3, 4, 5, 6);

        return ERROR;
        }

    /* create and fill the request */

    USBHST_FILL_BULK_URB(pUrb,
                         pDevice->pUsb2ClassDevice->hDevice,
                         bEndPointAddr,
                         pBuffer,
                         *pTransferLength,
                         USBHST_SHORT_TRANSFER_OK,
                         usb2SerialUrbCompleteCallback,
                         (VOID *)eventId,
                         USBHST_SUCCESS);

    USB2_SER_VDBG("usb2SerialBulkTransfer(): Submit an URB to read/write data\n",
                 1, 2, 3, 4, 5, 6);

    if ((hstStatus = usbHstURBSubmit(pUrb)) != USBHST_SUCCESS)
        {
        USB2_SER_ERR("usb2SerialBulkTransfer(): URB failed returned %d\n",
                     hstStatus, 2, 3, 4, 5, 6);

        errnoSet(EIO);

        OS_DESTROY_EVENT(eventId);

        return ERROR;
        }

    errnoSet (OK);

    /* wait for the event */

    if (OS_WAIT_FOR_EVENT(eventId,
            OS_CONVERT_MILLISECONDS_TO_WAIT_VALUE(pDevice->deviceTimeout))
        == ERROR)
        {
        USB2_SER_ERR("usb2SerialBulkTransfer: timeout\n", 1, 2, 3, 4, 5, 6);
        (void) usbHstURBCancel(pUrb);

        pUrb->pContext = NULL;

        OS_DESTROY_EVENT(eventId);
        return ERROR;
        }

    OS_DESTROY_EVENT(eventId);

    USB2_SER_VDBG("usb2SerialBulkTransfer(): Done, %d bytes with status %d\n",
                  pUrb->uTransferLength, pUrb->nStatus, 3, 4, 5, 6);

    *pTransferLength = pUrb->uTransferLength;

    return pUrb->nStatus;
    }

/*******************************************************************************
*
* usb2SerialCreate - create serial device data structures
*
* This routine is used to create serial device data structures by default
* settings.
*
* RETURNS: serial device pointer
*
* ERRNO: N/A
*
* \NOMANUAL
*/

LOCAL USB2_SERIAL_DEVICE * usb2SerialCreate
    (
    USB2_CLASS_DEVICE *         pUsb2ClassDevice,
    UINT8                       bInEndpointAddr,
    UINT8                       bOutEndpointAddr,
    UINT8                       bSetupEndpointAddr,
    USB2_SERIAL_ADAPTER_INFO *  pDriverInfo
    )
    {
    USB2_SERIAL_DEVICE *        pDevice = NULL;

    /* memory alloc for device structure */

    pDevice = (USB2_SERIAL_DEVICE *)OSS_CALLOC(sizeof(USB2_SERIAL_DEVICE));

    if (NULL == pDevice)
        {
        USB2_SER_ERR("usb2SerialCreate(): Basic data allocation failed\n",
                      1, 2, 3, 4, 5, 6);

        return NULL;
        }

    pDevice->pUsb2ClassDevice = pUsb2ClassDevice;

    /* out pipe */

    pDevice->pOutUrb = (USBHST_URB *)OSS_CALLOC(sizeof(USBHST_URB));

    if (NULL == pDevice->pOutUrb)
        {
        USB2_SER_ERR("usb2SerialCreate(): Basic data allocation failed\n",
                     1, 2, 3, 4, 5, 6);

        usb2SerialDelete(pDevice);
        return NULL;
        }

    if (bOutEndpointAddr != 0)
        {
        pDevice->bOutEndPointAddr = bOutEndpointAddr;
        }

    /* in pipe */

    pDevice->pInUrb = (USBHST_URB *)OSS_CALLOC(sizeof (USBHST_URB));

    if (pDevice->pInUrb == NULL)
        {
        USB2_SER_ERR("usb2SerialCreate(): Basic data allocation failed\n",
                     1, 2, 3, 4, 5, 6);

        usb2SerialDelete(pDevice);
        return NULL;
        }

    if (bOutEndpointAddr != 0)
        {
        pDevice->bInEndPointAddr = bInEndpointAddr;
        }

    /* setup pipe */

    pDevice->pSetupUrb = (USBHST_URB *)OSS_CALLOC(sizeof (USBHST_URB));

    if (pDevice->pSetupUrb == NULL)
        {
        USB2_SER_ERR("usb2SerialCreate(): Basic data allocation failed\n",
                     1, 2, 3, 4, 5, 6);

        usb2SerialDelete(pDevice);
        return NULL;
        }

    if (bOutEndpointAddr != 0)
        {
        pDevice->bSetupEndPointAddr = bSetupEndpointAddr;
        }

    pDevice->inDataEvent = OS_CREATE_EVENT(OS_EVENT_NON_SIGNALED);

    if (pDevice->inDataEvent == NULL)
        {
        USB2_SER_ERR("usb2SerialCreate(): Basic data allocation failed\n",
                     1, 2, 3, 4, 5, 6);

        usb2SerialDelete(pDevice);
        return NULL;
        }

    pDevice->terminateEvent = OS_CREATE_EVENT(OS_EVENT_NON_SIGNALED);

    if (pDevice->terminateEvent == NULL)
        {
        USB2_SER_ERR("usb2SerialCreate(): Basic data allocation failed\n",
                     1, 2, 3, 4, 5, 6);

        usb2SerialDelete(pDevice);
        return NULL;
        }

    pDevice->inDataMutex = semMCreate(SEM_Q_PRIORITY
                                      | SEM_INVERSION_SAFE
                                      | SEM_DELETE_SAFE);

    if (pDevice->inDataMutex == NULL)
        {
        USB2_SER_ERR("usb2SerialCreate(): Basic data allocation failed\n",
                     1, 2, 3, 4, 5, 6);

        usb2SerialDelete(pDevice);
        return NULL;
        }

    /* input data buffer */

    pDevice->inDataBuffer = OS_MALLOC(usb2SerialRingBfrSize);

    if (pDevice->inDataBuffer == NULL)
        {
        USB2_SER_ERR("usb2SerialCreate(): Malloc buffer error\n",
                     1, 2, 3, 4, 5, 6);

        usb2SerialDelete(pDevice);
        return NULL;
        }

    /* ring buffer */

    pDevice->hInRingBuffer = rngCreate(usb2SerialRingBfrSize);

    if (pDevice->hInRingBuffer == NULL)
        {
        USB2_SER_ERR("usb2SerialCreate(): pInRingBuffer allocation failed\n",
                     1, 2, 3, 4, 5, 6);

        usb2SerialDelete(pDevice);

        return NULL;
        }

    pDevice->pSelWakeupList =
        (SEL_WAKEUP_LIST *) OSS_CALLOC(sizeof(SEL_WAKEUP_LIST));

    if (pDevice->pSelWakeupList == NULL)
        {
        USB2_SER_ERR("usb2SerialCreate(): SelWakeupList allocation failed\n",
                     1, 2, 3, 4, 5, 6);

        usb2SerialDelete(pDevice);

        return NULL;
        }

    selWakeupListInit(pDevice->pSelWakeupList);

    pDevice->pDriverInfo = pDriverInfo;
    pDevice->uDeviceIndex = pUsb2ClassDevice->pNode->uDeviceIndex;

    pDevice->deviceTimeout = usb2SerialTimeOut;
    pDevice->baudRate = 0;

    return pDevice;
    }

/*******************************************************************************
*
* usb2SerialDelete - delete serial device data structures
*
* This routine is used to delete serial device data structures by default
* settings. Destory and free all buffer in the device structures.
*
* RETURNS: OK or ERROR
*
* ERRNO: N/A
*
* \NOMANUAL
*/

LOCAL STATUS usb2SerialDelete
    (
    USB2_SERIAL_DEVICE * pDevice  /* printer device to delete */
    )
    {
    if (pDevice)
        {
        if (pDevice->inTaskId != OS_THREAD_FAILURE)
            {
            return ERROR;
            }

        /* free the in urb */

        if (pDevice->pInUrb)
            {
            OSS_FREE(pDevice->pInUrb);
            pDevice->pInUrb = NULL;
            }

        /* free the out urb */

        if (pDevice->pOutUrb)
            {
            OSS_FREE(pDevice->pOutUrb);
            pDevice->pOutUrb = NULL;
            }

        /* free the setup urb */

        if (pDevice->pSetupUrb)
            {
            OSS_FREE(pDevice->pSetupUrb);
            pDevice->pSetupUrb = NULL;
            }

        /* delete the usb2ClassDevice structure */

        if (pDevice->pUsb2ClassDevice != NULL)
            {
            usb2ClassDeviceDelete(pDevice->pUsb2ClassDevice);
            pDevice->pUsb2ClassDevice = NULL;
            }

        if (pDevice->inDataEvent != NULL)
            {
            OS_DESTROY_EVENT(pDevice->inDataEvent);
            pDevice->inDataEvent = NULL;
            }

        if (pDevice->terminateEvent != NULL)
            {
            OS_DESTROY_EVENT(pDevice->terminateEvent);
            pDevice->terminateEvent = NULL;
            }

        if (pDevice->inDataMutex != NULL)
            {
            if (semTake(pDevice->inDataMutex, WAIT_FOREVER) == OK)
                semDelete(pDevice->inDataMutex);

            pDevice->inDataMutex = NULL;
            }

        if (pDevice->inDataBuffer != NULL)
            {
            OSS_FREE(pDevice->inDataBuffer);
            pDevice->inDataBuffer = NULL;
            }

        if (pDevice->hInRingBuffer)
            {
            rngDelete(pDevice->hInRingBuffer);
            pDevice->hInRingBuffer = NULL;
            }

        if (pDevice->pSelWakeupList)
            {
            selWakeupListTerm(pDevice->pSelWakeupList);

            OSS_FREE(pDevice->pSelWakeupList);

            pDevice->pSelWakeupList = NULL;
            }

        printf("Remove USB-to-Serial adapter device %s\n",
               pDevice->pDeviceGeneralName);

        if (pDevice->pDeviceGeneralName != NULL)
            {
            OSS_FREE(pDevice->pDeviceGeneralName);
            pDevice->pDeviceGeneralName = NULL;
            }

        OSS_FREE(pDevice);
        }
    
    return OK;
    }

/*******************************************************************************
*
* usb2SerialTestInTask - input routine for serial test
*
* This routine is input task function for serial test
*
* RETURNS: N/A
*
* ERRNO: N/A
*
* \NOMANUAL
*/

LOCAL VOID usb2SerialTestInTask
    (
    int fd
    )
    {
    char c;

    if (fd == NONE)
        {
        printf("parameter error\n");
        return;
        }
    while (TRUE)
        {
        if (read(fd, &c, 1) > 0)
            (void)putchar(c);
        }
    }

/*******************************************************************************
*
* usb2SerialTest - test a device input and output
*
* This routine is a test function used to test a device input and output.
*
* RETURNS: N/A
*
* ERRNO: N/A
*
* \NOMANUAL
*/

VOID usb2SerialTest
    (
    char *devName
    )
    {
    int     fd;
    char    b[512];
#if ((_WRS_VXWORKS_MAJOR == 6) && (_WRS_VXWORKS_MINOR >= 9)) || \
    (_WRS_VXWORKS_MAJOR > 6)
    TASK_ID inTaskId;
#else
    int     inTaskId;
#endif

    /* open device */
    fd = open(devName, O_RDWR, 0);

    if (fd == NONE)
        {
        printf("Open device %s failed\n", devName);
        return;
        }

    /* create input task */

    inTaskId = taskSpawn("tUsb2SerialTestInTask", 100, 0, 0x4000,
                         (FUNCPTR)usb2SerialTestInTask, fd,
                         0, 0, 0, 0, 0, 0, 0, 0, 0);

    if (inTaskId == OS_THREAD_FAILURE)
        {
        printf("fail to spwan new task\n");
        close(fd);
        return;
        }

    printf("Type \"quit\" to exit this mode\n");

    while(TRUE)
        {
        printf("Input>");
        memset(b, 0, sizeof(b));
        fgets(b, sizeof(b), stdin);

        if (strcmp(b, "quit\n") == 0
            || strcmp(b, "quit\n\r") == 0
            || strcmp(b, "quit\r\n") == 0
            || strcmp(b, "quit\r") == 0)
            {
            taskDelete(inTaskId);
            break;
            }

        if (strlen(b) != write(fd, b, strlen(b)))
            {
            printf ("Write %ld incompleted\n",strlen(b));
            }
        }

    close(fd);
    }

